﻿using System;
using System.ServiceModel;
using BusinessLogic;
using WcfServiceLibrary.FaultContract;
using WcfServiceLibrary.Messages;
using WcfServiceLibrary.Messages.Base;
using WcfServiceLibrary.Messages.Options;

namespace WcfServiceLibrary
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class Service : IService
    {
        public TokenResponse GetToken(TokenRequest request)
        {
            ValidateHeader(request.RequestHeader, Validate.ClientTag | Validate.RequestId);

            var response = new TokenResponse
                               {
                                   ResponseHeader = new ResponseHeader { CorrespondingId = request.RequestHeader.RequestId }
                               };

            var token = Guid.NewGuid().ToString();
            SafeContainer.Instance.AddToken(token);
            response.SecurityToken = token;
            response.ResponseHeader.Status = OperationStatus.Success;
            return response;
        }

        public IAsyncResult BeginGetToken(TokenRequest request, AsyncCallback callback, object asyncState)
        {
            return new AsyncResult<TokenRequest, TokenResponse>(GetToken, request, callback, asyncState);
        }

        public TokenResponse EndGetToken(IAsyncResult result)
        {
            TokenResponse response;

            using (var getResult = (AsyncResult<TokenRequest, TokenResponse>)result)
            {
                response = getResult.FetchResultsFromAsyncOperation();
            }
            return response;
        }

        public PersonResponse GetPerson(PersonGetRequest request)
        {
            ValidateHeader(request.RequestHeader, Validate.All);

            var response = new PersonResponse
                               {
                                   ResponseHeader = new ResponseHeader { CorrespondingId = request.RequestHeader.RequestId }
                               };
            switch (request.Criteria.Options)
            {
                case PersonOptions.All:
                    response.Objects = PersonLogic.GetAll();
                    break;
                case PersonOptions.Id:
                    response.Objects = PersonLogic.GetById(request.Criteria.Value);
                    break;
            }

            response.ResponseHeader.Status = OperationStatus.Success;
            return response;
        }

        public IAsyncResult BeginGetPerson(PersonGetRequest request, AsyncCallback callback, object asyncState)
        {
            return new AsyncResult<PersonGetRequest, PersonResponse>(GetPerson, request, callback, asyncState);
        }

        public PersonResponse EndGetPerson(IAsyncResult result)
        {
            PersonResponse response;

            using (var getResult = (AsyncResult<PersonGetRequest, PersonResponse>)result)
            {
                response = getResult.FetchResultsFromAsyncOperation();
            }
            return response;
        }

        [Flags]
        enum Validate
        {
            ClientTag = 0,
            SecurityToken = 1,
            RequestId = 2,
            All = ClientTag | SecurityToken | RequestId
        }

        static void ValidateHeader(RequestHeader header, Validate validate)
        {
            if ((Validate.ClientTag & validate) == Validate.ClientTag)
            {
                if (header.ClientTag != "ABC123")
                {
                    throw new FaultException<ServiceFault>(new ServiceFault("Zły tag klienta. Proszę przesłać poprawny tag."), "Nieprawidłowy tag klienta");
                }
            }
            if ((Validate.SecurityToken & validate) == Validate.SecurityToken)
            {
                if (header.SecurityToken == null || !SafeContainer.Instance.ValidToken(header.SecurityToken))
                {
                    throw new FaultException<ServiceFault>(new ServiceFault("Nieprawidłowy token bezpieczeństwa. Proszę pobrać nowy token"), "Nieprawidłowy token bezpieczeństwa");
                }
            }
            if ((Validate.RequestId & validate) == Validate.RequestId)
            {
                if (header.RequestId == null || !SafeContainer.Instance.AddRequestId(header.RequestId))
                {
                    throw new FaultException<ServiceFault>(new ServiceFault("Żądanie zostało już obsłużone"), "Powtórzone requestId");
                }
            }
        }
    }
}
